package cn.birdbaby.plugins.refactorgen;

import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.listeners.RefactoringElementListener;
import net.jcip.annotations.NotThreadSafe;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.List;

/**
 * 重命名/移动domain entity的文件夹时，连带修改@Gen注解中的包名
 * 重命名/移动domain entity到新的文件夹时，连带修改@Gen注解中的包名
 * todo 不知道为什么掉了两次
 * 还不确定是否会发生并发调用，先标记一下
 */
@NotThreadSafe
public class BBRefactoringElementListener implements RefactoringElementListener {

    private BBRefactorContext context;

    //这里传进来的element，缺少classes信息；和下面方法进来的，数据不一样，
    public BBRefactoringElementListener() {
    }


    @Override
    public void elementRenamed(@NotNull PsiElement newElement) {
        // 在这里添加元素被重命名时的逻辑
        context = new BBRefactorContext(newElement);
        if (newElement instanceof PsiClass) {
            //Gen中的包名，以类所在目录的名称为准。所以类被重命名时，不对Gen中的数据进行操作
            PsiClass psi = (PsiClass) newElement;
        } else if (newElement instanceof PsiPackage) {
            modifyGenPkgNameWhenRefactorPacakge();
        }

    }

    private void refactorGenWhenRemoveClass(PsiClass psiClass) {
        /**
         1. 被重命名的时候，包名是没有改变的，这时候可以从包名中拿到旧的packageName
         2. 根据旧的className，查找pkgName中的字段，将domain.xxxx.subPkgName中的内容替换成新的className
         */
        String packageName = ReadAction.compute(() -> {
            PsiFile psiFile = psiClass.getContainingFile();
            if (psiFile instanceof PsiJavaFile) {
                PsiPackageStatement psiPackageStatement = ((PsiJavaFile) psiFile).getPackageStatement();
                return psiPackageStatement.getPackageName();
            }
            return "";
        });
        String oldName = packageName.substring(packageName.lastIndexOf("."));

        PsiAnnotation[] annotations = psiClass.getAnnotations();
        for (PsiAnnotation annotation : annotations) {
            PsiAnnotationMemberValue value = annotation.findAttributeValue("pkgName");
            if (value != null) {
                String oldPkgName = value.getText();
                String newPkgName = oldPkgName.replace("." + oldName + ".", "." + psiClass.getName() + ".");
                value.replace(createNewValue(annotation, newPkgName));
            }
        }
    }

    private void modifyGenPkgNameWhenRefactorPacakge() {
        /**
         * 目前只考虑一级目录的场景
         1. 判断dir是否是domain dir的下级目录 && 判断 dir 中是否包含@Table+@Entity标记的类
         2. 获取旧的包名
         3. 使用新的dir名称进行替换
         */
        List<PsiClass> psiClasses = context.collectDomainFiles();
        if (psiClasses.isEmpty()) return;
        for (PsiClass psiClass : psiClasses) {
            modifyGenPkgName(psiClass);
        }
    }

    /**
     * @param psiElement 也可以不传值，直接传取值的逻辑，拓展性更强
     */
    private void modifyGenPkgName(PsiElement psiElement) {
        Collection<PsiAnnotation> psiAnnotations = ReadAction.compute(() ->
                PsiTreeUtil.findChildrenOfAnyType(psiElement, PsiAnnotation.class)
        );
        for (PsiAnnotation annotation : psiAnnotations) {
            PsiAnnotationMemberValue value = annotation.findAttributeValue("pkgName");
            if (value != null) {
                String newPkgName = value.getText().replace(context.getOldName(), context.getNewName());
                //any modifications to the PSI must be performed on the EDT (Event Dispatch Thread), which is the main thread that handles all UI updates and ensures the responsiveness of the IDE.
                //idea的操作中，也有事务的概念，保证对psiElement集合操作的一致性，以及操作失败后的回退操作
                WriteCommandAction.runWriteCommandAction(psiElement.getProject(), () -> {
                    // Perform PSI modification here
                    value.replace(createNewValue(annotation, newPkgName));
                });
            }
        }
    }


    private PsiAnnotationMemberValue createNewValue(PsiAnnotation annotation, String newPkgName) {
        // 创建新的值，例如字符串
        PsiExpression newValue = JavaPsiFacade.getElementFactory(annotation.getProject())
                .createExpressionFromText(newPkgName, annotation);
        return newValue;
    }

    private void modifyGenPkgNameWhenRefactorClass(){
        List<PsiClass> psiClasses = context.collectDomainFiles();
        if (psiClasses.isEmpty()) return;
        for (PsiClass psiClass : psiClasses) {
            modifyGenPkgName(psiClass);
        }
    }
    /**
     1.domain被移动时：将Gen注解中旧的包名，修改为新目录对应的包名
     2.package被移动时：将package下的domain中Gen注解中旧的包名，修改为新目录对应的包名
     2其实是1的批次操作
     提炼出原子操作，可以很方便进行组合
     * @param newElement
     */
    @Override
    public void elementMoved(@NotNull PsiElement newElement) {
        // 在这里添加元素只被移动时的逻辑
        context=new BBRefactorContext(newElement);
        if(newElement instanceof PsiClass){
            modifyGenPkgNameWhenRefactorClass();
        } else if (newElement instanceof PsiPackage) {
            modifyGenPkgNameWhenRefactorPacakge();
        }
    }

}
